using System;
using System.Drawing;

namespace Altairis.FluffyCloud.Storage {

    /// <summary>
    /// This class contains miscellaneous utilities for working with images in web applications.
    /// </summary>
    internal static class ImageExtensionMethods {
        #region Enums

        /// <summary>
        /// Represents horizontal position of resized image on canvas.
        /// </summary>
        public enum HorizontalAlign { Left, Center, Right }

        /// <summary>
        /// Represents vertical position of resized image on canvas.
        /// </summary>
        public enum VerticalAlign { Top, Middle, Bottom }

        #endregion Enums

        /// <summary>
        /// Resizes image exactly to specified size, cropping excess areas.
        /// </summary>
        /// <param name="originalImage">Original image.</param>
        /// <param name="newSize">Maximum new size.</param>
        /// <returns>Image resized to fit into specified maximum dimension.</returns>
        public static Image ResizeAndCrop(this Image originalImage, Size newSize) {

            // Prepare canvas
            Bitmap targetImage = new Bitmap(newSize.Width, newSize.Height);
            Graphics gph = Graphics.FromImage(targetImage);
            gph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            gph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            gph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            // Prepare size and position
            var s = GetRealCropSize(originalImage.Size, newSize);
            var x = (newSize.Width - s.Width) / 2;
            var y = (newSize.Height - s.Height) / 2;

            // Draw image
            gph.DrawImage(originalImage, x, y, s.Width, s.Height);
            return targetImage;
        }

        /// <summary>
        /// Resizes image to specified maximum size while retaining proportions.
        /// </summary>
        /// <param name="originalImage">Original image.</param>
        /// <param name="newSize">Maximum new size.</param>
        /// <returns>Image resized to fit into specified maximum dimension.</returns>
        public static Image ResizeImage(this Image originalImage, Size newSize) {

            // Prepare canvas
            Size realNewSize = GetRealFitSize(originalImage.Size, newSize);
            Bitmap targetImage = new Bitmap(realNewSize.Width, realNewSize.Height);
            Graphics gph = Graphics.FromImage(targetImage);
            gph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            gph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            gph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            // Draw image
            gph.DrawImage(originalImage, 0, 0, realNewSize.Width, realNewSize.Height);
            return targetImage;
        }

        /// <summary>
        /// Resizes image to specified maximum size while retaining proportions and fills remaining space with specified color.
        /// </summary>
        /// <param name="originalImage">Original image.</param>
        /// <param name="newSize">New image size.</param>
        /// <param name="backgroundColor">Color to fill unused parts of image.</param>
        /// <returns>Image resized to fit into specified maximum dimension. Image is vertically and horizontally centered and remaining space is filled by specified background color.</returns>
        public static Image ResizeImage(this Image originalImage, Size newSize, Color backgroundColor) {
            return ResizeImage(originalImage, newSize, backgroundColor, HorizontalAlign.Center, VerticalAlign.Middle);
        }

        /// <summary>
        /// Resizes image to specified maximum size while retaining proportions and fills remaining space with specified color.
        /// </summary>
        /// <param name="originalImage">Original image.</param>
        /// <param name="newSize">New image size.</param>
        /// <param name="backgroundColor">Color to fill unused parts of image.</param>
        /// <param name="hAlign">Horizontal alignment of image.</param>
        /// <param name="vAlign">Vertical alignment of image.</param>
        /// <returns>Image resized to fit into specified maximum dimension. Remaining space is filled by specified background color.</returns>
        public static Image ResizeImage(this Image originalImage, Size newSize, Color backgroundColor, HorizontalAlign hAlign, VerticalAlign vAlign) {
            if (originalImage.Size == newSize) return originalImage;

            // Prepare canvas
            Bitmap targetImage = new Bitmap(newSize.Width, newSize.Height);
            Graphics gph = Graphics.FromImage(targetImage);
            gph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            gph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            gph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            // Draw background
            gph.FillRectangle(new SolidBrush(backgroundColor), 0, 0, newSize.Width, newSize.Height);

            // Set size and position of image
            Size realNewSize = GetRealFitSize(originalImage.Size, newSize);
            Point loc = new Point(0, 0);
            switch (vAlign) {
                case VerticalAlign.Middle:
                    loc.Y = (newSize.Height - realNewSize.Height) / 2;
                    break;

                case VerticalAlign.Top:
                    loc.Y = newSize.Height - realNewSize.Height;
                    break;
                default:
                    break;
            }
            switch (hAlign) {
                case HorizontalAlign.Center:
                    loc.X = (newSize.Width - realNewSize.Width) / 2;
                    break;

                case HorizontalAlign.Right:
                    loc.X = newSize.Width - realNewSize.Width;
                    break;
                default:
                    break;
            }

            // Draw image
            gph.DrawImage(originalImage, new Rectangle(loc, realNewSize));
            return targetImage;
        }

        // Helper methods

        private static Size GetRealCropSize(SizeF originalSize, SizeF maxSize) {
            float ratio = Math.Max(maxSize.Width / originalSize.Width, maxSize.Height / originalSize.Height);
            return new Size(System.Convert.ToInt32(originalSize.Width * ratio), System.Convert.ToInt32(originalSize.Height * ratio));
        }

        private static Size GetRealFitSize(SizeF originalSize, SizeF maxSize) {
            SizeF vRatio = new SizeF(maxSize.Width / originalSize.Width, maxSize.Height / originalSize.Height);
            float ratio = Math.Min(vRatio.Width, vRatio.Height);
            return new Size(System.Convert.ToInt32(originalSize.Width * ratio), System.Convert.ToInt32(originalSize.Height * ratio));
        }
    }
}